package com.openatc.optimize.fixedtimeplan.model.control;


import com.alibaba.fastjson.JSON;
import com.openatc.optimize.fixedtimeplan.config.cross.*;

import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;

import java.util.*;

/**
 * @author mz
 * @version 1.0
 * @date 2021/10/26 8:52
 */
@Data
@AllArgsConstructor
@Slf4j
@Builder
public class Cross {







    private Map<Integer, Phase> phaseMap;
    private String agentid;
    private String unitid;

    private double totalY;
    private long cycle;



    //路口初始化配置
    private CrossConfig crossConfig;


    public void updatePhaseMap(){
        for(Phase phase:crossConfig.getPhases()){
            phaseMap.put(phase.getId(), phase);
        }
    }



    private double updateTotalY(){
        double y = 0;
        for(Barrier barrier:crossConfig.getBarriers()){
            calcYofBarrier(barrier);
            log.info("y of barrer {} is {}", barrier.getNum(), barrier.getY());
            y += barrier.getY();
        }
        return y;
    }

    private void calcYofBarrier(Barrier barrier){
        double Y = 0;
        for(BarrierItem item:barrier.getItems()){
            List<Integer> ids = item.getData();
            double tempY = 0;
            for(Integer id:ids){
                tempY += phaseMap.get(id).Y();
            }
            log.info("temp Y of barrer {} is {}", barrier.getNum(), tempY);
            if(tempY > Y){
                Y = tempY;
            }
        }

        /*
        for(List<Integer> ids:barrier.getPhases()){
            double tempY = 0;
            for(Integer id:ids){
                tempY += phaseMap.get(id).Y();
            }
            log.info("temp Y of barrer {} is {}", barrier.getNum(), tempY);
            if(tempY > Y){
                Y = tempY;
            }
        }

         */
        barrier.setY(Y);
    }

    private void assignDurationOfBarrier(){
        long temototalbarrierlength = 0;
        for(int barrierno = 0; barrierno<crossConfig.getBarriers().size(); barrierno++){

            Barrier barrier = crossConfig.getBarriers().get(barrierno);
            if(barrierno != crossConfig.getBarriers().size() - 1){
                long tempduration = Math.round(barrier.getY()/totalY*cycle);
                barrier.setDuration(tempduration);
                temototalbarrierlength += tempduration;
            }else{
                barrier.setDuration(cycle - temototalbarrierlength);
            }

            log.info("duration of barrier {} is {}", barrier.getNum(), barrier.getDuration());
            for(BarrierItem item:barrier.getItems()){
                List<Integer> ids = item.getData();
                double tempY = 0;
                for(Integer id : ids){
                    tempY += phaseMap.get(id).Y();
                }


                int temptotalduration = 0;
                for(int i = 0; i< ids.size(); i++){
                    int id = ids.get(i);
                    if(i != ids.size() - 1){

                        long phaseduration = Math.round(barrier.getDuration() * phaseMap.get(id).Y()/tempY);
                        temptotalduration += phaseduration;
                        phaseMap.get(id).setDuration(phaseduration);
                    }else{
                        phaseMap.get(id).setDuration(barrier.getDuration() - temptotalduration);
                    }
                    log.info("initial phase:{}, duration:{}", id, phaseMap.get(id).getDuration());
                }

                /*
                for(Integer id : ids){
                    phaseMap.get(id).setDuration(Math.round(barrier.getDuration()*phaseMap.get(id).Y()/tempY));
                }

                 */
            }

            /**
             * 根据最大最小绿调整相位长度
             * */
            barrier.adjustPhaseDuration(phaseMap);
        }


    }


    public FixedtimePlan calcFixedtimePlan(){


        totalY = updateTotalY();

        log.info("Y of agent:{} is {}", agentid, totalY);
        double L = (crossConfig.getLoss()+crossConfig.getRed()) * crossConfig.getRings().get(0).getSequence().size();
        log.info("loss time of agentid: {} is {}", agentid, L);


        totalY  = totalY > 0.95 ? 0.95 : totalY;
        if(totalY < 0.9){

            //webster模型
            cycle = (long) Math.ceil((1.5 * L + 5) / (1 - totalY));

        }else{

            //ARRB模型
            cycle = (long) Math.ceil((1.4 * L + 6) / (1- totalY));
        }

        log.info("cycle of {} is {}", agentid, cycle);


        assignDurationOfBarrier();

        updateCycle();

        log.info("final cycle of {} is {}", agentid, cycle);
        FixedtimePlan fixedtimePlan = FixedtimePlan.builder()
                .cycle(Math.round(cycle))
                .phase(new ArrayList<>(phaseMap.values()))
                .build();




        return fixedtimePlan;
    }


    /**
     * 最大最小绿调整后，重新计算周期长
     * */
    private void updateCycle(){
        int tempcycle = 0;
        for(Barrier barrier:crossConfig.getBarriers()){
            tempcycle += barrier.getDuration();
        }
        cycle = tempcycle;
    }

}
